﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Fluq
{
	public static class DependencyBuilderExtensions
	{
		public static IDependencyBuilder As<TAs>(this IDependencyBuilder source)
		{
			return source.Coalesce(d => d.Coalesce(new DependencyKey(typeof(TAs), d.Key.Id)));
		}

		public static IDependencyBuilder Singleton(this IDependencyBuilder source)
		{
			return source.Coalesce(d => d.Coalesce(lifestyle: new SingletonLifestyle()));
		}

		public static IDependencyBuilder Transient(this IDependencyBuilder source)
		{
			return source.Coalesce(d => d.Coalesce(lifestyle: new TransientLifestyle()));
		}

		public static IDependencyBuilder PerGraph(this IDependencyBuilder source)
		{
			return source.Coalesce(d => d.Coalesce(lifestyle: new PerGraphLifestyle()));
		}

		public static IDependencyBuilder ParameterValue(this IDependencyBuilder source, string name, object value)
		{
			IDependencyParameter parameter = new DependencyParameter(c => value, p => String.Equals(p.Name, name, StringComparison.Ordinal));

			return source.Coalesce(d => d.Coalesce(parameters: d.Parameters.Concat(new IDependencyParameter[] { parameter })));
		}

		private static IDependencyBuilder Coalesce(this IDependencyBuilder source, Func<IDependency, IDependency> decorator)
		{
			source.Decorate(e => e.Select((d => decorator(d))));

			return source;
		}

		public static IDependencyBuilder AddType(this IDependencyBuilder source, Type type)
		{
			return source.AddChildBuilder(FromType(type));
		}

		public static IDependencyBuilder AddType<T>(this IDependencyBuilder source)
		{
			return source.AddType(typeof(T));
		}

		public static IDependencyBuilder AddType<T>(this IDependencyBuilder source, Func<IDependencyContainer, IEnumerable<IDependencyParameter>, T> factory)
		{
			return source.AddChildBuilder(FromType(typeof(T), (c, p) => factory(c, p)));
		}

		public static IDependencyBuilder AddTypes(this IDependencyBuilder source, IEnumerable<Type> types)
		{
			return source.AddChildBuilder(types.SelectMany(t => FromType(t)));
		}

		public static IDependencyBuilder AddAmbientTypes(this IDependencyBuilder source)
		{
			return source.AddAmbientTypes(type => true);
		}

		public static IDependencyBuilder AddAmbientTypes(this IDependencyBuilder source, Func<Type, bool> predicate)
		{
			return source.AddTypes(AppDomain.CurrentDomain.GetAssemblies().SelectMany(a => a.GetTypes().Where(predicate)));
		}

		public static IDependencyBuilder AddAmbientTypesImplementing<T>(this IDependencyBuilder source)
		{
			Type typeToFind = typeof(T);

			return source.AddAmbientTypes(t => t.IsInstanceOfType(typeToFind));
		}

		private static IEnumerable<IDependency> FromType(Type type)
		{
			return FromType(type, (c, p) => c.Composer.Compose(c, type, p));
		}

		private static IEnumerable<IDependency> FromType(Type type, Func<IDependencyContainer, IEnumerable<IDependencyParameter>, object> factory)
		{
			yield return new Dependency(new DependencyKey(type), factory, new TransientLifestyle(), Enumerable.Empty<IDependencyParameter>());
		}
	}
}
